package com.spring.nio.core.event.handler;

import com.spring.nio.business.user.dto.UserDTO;
import com.spring.nio.business.user.service.UserService;
import com.spring.nio.core.channel.context.SocketChannelContext;
import com.spring.nio.core.channel.handler.AbstractChannelHandler;
import com.spring.nio.core.event.model.ChannelCacheDataModel;
import com.spring.nio.core.event.model.EventModel;
import com.spring.nio.core.startup.NioServerContext;
import com.spring.nio.service.file.model.structure.FileTransportTaskDTO;
import com.spring.nio.util.LocalTime;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author: YSFY
 * @date: 2021-03-24 14:31
 * @pacage_name: com.spring.nio.core.event.handler
 * @class_name: AbstractEventHandler
 * @description: 抽象的公共事件处理
 */

@Slf4j
@SuppressWarnings("all")
public abstract class AbstractEventHandler<T extends EventModel> implements EventHandler {

    /**
     * 通道缓存数据 key： 通道地址 value: 当前通道对应的缓存数据
     * */
    public static final Map<String, ChannelCacheDataModel> channelDataMap = new ConcurrentHashMap<>();

    /**
     * 指向下一个handler指针
     * */
    private AbstractEventHandler nextEventHandler;

    /**
     * 校验SelectionKey的合法性
     * @Param t EventModel
     * @return boolean
     * */
    public Boolean checkEventValid(T t) {
        if(!t.getSelectionKey().isValid()) {
            log.info("--> AbstractEventHandler | --> selectionKey is Invalid");
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    /**
     * 获取当前通道channel的数据缓存对象: 有可能某个通道一次触发读事件并不能读取完整数据, 所以此处需要获取的通道的数据缓存对象ChannelCacheDataModel
     * 则直接从缓存中获取
     * @param currentChannelEventModel
     * @return ChannelCacheDataModel
     */
    public final ChannelCacheDataModel getCurrentChannelCacheModel(EventModel currentChannelEventModel) {
        ChannelCacheDataModel channelCacheDataModel = null;
        String currentChannelAddress = currentChannelEventModel.getRemoteAddress();
        if(!channelDataMap.containsKey(currentChannelAddress)) {
            channelCacheDataModel = new ChannelCacheDataModel();
            channelCacheDataModel.setChannelAddress(currentChannelAddress);
            channelDataMap.put(currentChannelAddress, channelCacheDataModel);
        } else {
            channelCacheDataModel = channelDataMap.get(currentChannelAddress);
        }

        return channelCacheDataModel;
    }

    /**
     * 清理指定通道对应的缓存数据
     * @param address
     * */
    public static void clearChannelCache(String address) {
        AbstractEventHandler.channelDataMap.remove(address, AbstractEventHandler.channelDataMap.get(address));
    }

    /**
     * 校验socketChannelContext产生的I/O异常来自于聊天还是文件(具体为哪个文件产生的异常)
     * @param socketChannelContext
     * @param e
     * */
    public void logIOExceptionReason(SocketChannelContext socketChannelContext, Exception e) {
        // 校验是否是用户聊天通道IO异常
        if(!CollectionUtils.isEmpty(UserService.onlineUser)) {
            Map<Long, List<UserDTO>> onlineUserMap = (new ArrayList<UserDTO>(UserService.onlineUser.values())).stream().collect(Collectors.groupingBy(UserDTO::getId));
            for(List<UserDTO> userDtos : onlineUserMap.values()) {
                UserDTO userDto = userDtos.get(0);
                if(userDto.getChatSocketChannelContext().equals(socketChannelContext)) {
                    String address= socketChannelContext.getRemoteAddress();
                    log.error("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] AbstractEventHandler | --> 当前用户 = [{}] 对应聊天通道 = [{}] 出现IO异常, error = [{}]", userDto.getUserName(), address, e.getMessage());
                    return;
                } else {
                    // 尝试判断是否是当前用户上传任务集的通道异常
                    if(!CollectionUtils.isEmpty(userDto.getUploadFileMap())) {
                        Map<String, List<FileTransportTaskDTO>> taskMap = (new ArrayList<FileTransportTaskDTO>(userDto.getUploadFileMap().values())).stream().collect(Collectors.groupingBy(FileTransportTaskDTO::getFileTag));
                        for(List<FileTransportTaskDTO> tasks : taskMap.values()) {
                            FileTransportTaskDTO task = tasks.get(0);
                            if(task.getSocketChannelContext().equals(socketChannelContext)) {
                                String address= socketChannelContext.getRemoteAddress();
                                log.error("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] AbstractEventHandler | --> 当前用户[{}]对应文件 = [{}] 上传通道 = [{}] 出现IO异常, error = [{}]",
                                    userDto.getUserName(), task.getFile().getName(), address, e.getMessage());
                                return;
                            }
                        }
                    }

                    // 尝试判断是否是当前用户下载任务集中的通道异常
                    if(!CollectionUtils.isEmpty(AbstractChannelHandler.fileDownloadThreadMap)) {
                        for(Map<String, Object> map : AbstractChannelHandler.fileDownloadThreadMap.values()) {
                            FileTransportTaskDTO task = (FileTransportTaskDTO) map.get("TASK");
                            if(task.getSocketChannelContext().equals(socketChannelContext)) {
                                String address= socketChannelContext.getRemoteAddress();
                                log.error("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] AbstractEventHandler | --> 当前用户[{}]对应文件 = [{}] 下载通道 = [{}] 出现IO异常, error = [{}]",
                                    userDto.getUserName(), task.getFile().getName(), address, e.getMessage());
                                return;
                            }
                        }
                    }
                }
            }
        }

        // 判断是否是文件产生的异常
    }

    public AbstractEventHandler getNextEventHandler() {
        return nextEventHandler;
    }

    public void setNextEventHandler(AbstractEventHandler nextEventHandler) {
        this.nextEventHandler = nextEventHandler;
    }
}
